#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include "locker.h"
#include "threadpool.h"
#include "http_conn.h"
#include <iostream>
using namespace std;

#define MAX_FD 65536
#define MAX_EVENT_NUMBER 10000

// 
extern void addfd(int epollfd, int fd, bool one_shot); // 在http_conn.cpp中
extern void removefd(int epollfd, int fd); // 在http_coon.cpp中
// 
void addsig(int sig, void(handler)(int)){ // handler回调函数，用来处理信号
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler =handler;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, NULL) != -1);
}

int main(int argc, char* argv[]){

    if(argc<=1){
        cout<<"正确格式："<<argv[0]<<" "<<"端口号"<<endl;
        return 1;
    }

    int port = atoi(argv[1]);
    addsig(SIGPIPE, SIG_IGN);

    threadpool<http_conn>* pool = nullptr; // TODO
    try{
        pool=new threadpool<http_conn>;
    }catch(...){ // catch(...)省略号的作用是表示捕获所有类型的异常
        return 1;
    }

    http_conn* users=new http_conn[MAX_FD];
    // 创建套接字http://c.biancheng.net/view/2131.html
    int listenfd=socket(PF_INET, SOCK_STREAM, 0);

    int ret=0;
    struct sockaddr_in address;         // 后续bind操作
    address.sin_addr.s_addr=INADDR_ANY; // INADDR_ANY转换过来是0.0.0.0→泛指本机,表示本机的所有IP(因为有些机器有多张网卡)
    address.sin_family=AF_INET;         // ipv4
    address.sin_port=htons(port);

    // 端口复用
    int reuse=1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); //
    ret=bind(listenfd, (struct sockaddr*)&address, sizeof(address));
    ret=listen(listenfd, 5);

    // 创建epoll对象，创建事件数组，添加
    epoll_event events[MAX_EVENT_NUMBER]; // 数组名→events,数组元素→epoll_event结构体
    int epollfd=epoll_create(5);
    // 添加到epoll对象中
    addfd(epollfd, listenfd, false);
    http_conn::m_epollfd=epollfd; // 所有socket上的事件都被注册到同一个epoll内核事件中，所以设置成静态的
    
    while(true){

        int number=epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1); // 发生的事件数

        if((number<0) && (errno!=EINTR)){ // 如果read()读到数据为0，那么就表示文件读完了，如果在读的过程中遇到了中断则read()应该返回-1，同时置errno为EINTR
            cout<<"epoll调用失败"<<endl;
            break;
        }

        for(int i=0;i<number;i++){

            int sockfd=events[i].data.fd;

            if(sockfd==listenfd){ // 监听到了什么

                struct sockaddr_in client_address;
                socklen_t client_addrlength=sizeof(client_address);
                int connfd=accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);

                if(connfd<0){ // connfd存储accept函数的返回值
                    cout<<"errno is: "<<errno<<endl;
                    continue;
                }

                if(http_conn::m_user_count>=MAX_FD){ // user_count>=65536
                    close(connfd);
                    continue;
                }
                users[connfd].init(connfd, client_address); // http_conn数组

            }else if(events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)){

                users[sockfd].close_conn();

            }else if(events[i].events & EPOLLIN){ // EPOLLIN事件

                if(users[sockfd].read()){ // bool read();// 非阻塞读
                    pool->append(users+sockfd); // users是数组指针，可以当做数组名使用，这里将指定的http_conn加入到线程池中
                }else{
                    users[sockfd].close_conn();
                }

            }else if(events[i].events & EPOLLOUT){ // 对于EPOLLOUT: 如果状态改变了[比如 从满到不满],只要输出缓冲区可写就会触发
                
                if(!users[sockfd].write()){
                    users[sockfd].close_conn();
                }
            }
        }
    }

    close(epollfd);
    close(listenfd);
    delete[] users;
    delete pool;
    return 0;
}